import json
import os
import uuid
from flask import (Flask, jsonify, request, render_template, redirect,
                   url_for, flash, send_from_directory)
from werkzeug.utils import secure_filename
app = Flask(__name__, template_folder='templates') # 指定模板文件夹
app.secret_key = 'a_secret_key_for_file_management' # flash消息需要secret_key

# --- 文件上传配置 ---
UPLOAD_FOLDER = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'uploads')
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
# 确保上传文件夹存在
os.makedirs(UPLOAD_FOLDER, exist_ok=True)


# --- 数据文件路径 ---
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
SOFTWARE_LINKS_JSON_PATH = os.path.join(BASE_DIR, 'software_links.json')
COMMANDS_JSON_PATH = os.path.join(BASE_DIR, 'commands.json')

# --- 数据加载 ---
try:
    with open(SOFTWARE_LINKS_JSON_PATH, 'r', encoding='utf-8') as f:
        software_data = json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
    software_data = {}

# --- 命令管理辅助函数 ---
def _read_commands():
    """读取并返回所有命令"""
    try:
        with open(COMMANDS_JSON_PATH, 'r', encoding='utf-8') as f:
            return json.load(f)
    except (FileNotFoundError, json.JSONDecodeError):
        return []

def _write_commands(commands):
    """将命令列表写入文件"""
    with open(COMMANDS_JSON_PATH, 'w', encoding='utf-8') as f:
        json.dump(commands, f, indent=4, ensure_ascii=False)


@app.route('/')
def search():
    """
    软件链接查询页面
    """
    return render_template('search.html')

# --- 文件管理路由 ---

@app.route('/files')
def files():
    """
    文件管理页面，列出已上传的文件。
    """
    try:
        uploaded_files = os.listdir(app.config['UPLOAD_FOLDER'])
        uploaded_files = [f for f in uploaded_files if not f.startswith('.')]
    except FileNotFoundError:
        uploaded_files = []
    return render_template('files.html', files=uploaded_files)

@app.route('/uploads/<filename>')
def uploads_file(filename):
    """
    提供上传文件的直接访问链接。
    """
    return send_from_directory(app.config['UPLOAD_FOLDER'], filename)

@app.route('/upload', methods=['POST'])
def upload_file():
    """
    处理文件上传。
    """
    if 'file' not in request.files or request.files['file'].filename == '':
        flash('未选择有效文件')
        return redirect(url_for('files'))
    
    file = request.files['file']
    filename = os.path.basename(file.filename)
    
    if not filename:
        flash('文件名无效')
        return redirect(url_for('files'))

    file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
    flash(f'文件 "{filename}" 上传成功！')
    return redirect(url_for('files'))

@app.route('/view/<filename>')
def view_file(filename):
    """
    查看文件内容。
    """
    filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
    try:
        with open(filepath, 'r', encoding='utf-8') as f:
            content = f.read()
    except FileNotFoundError:
        flash(f'文件 "{filename}" 不存在。')
        return redirect(url_for('files'))
    except Exception as e:
        content = f"无法读取文件内容: {e}"
    
    return render_template('view.html', filename=filename, content=content)

@app.route('/delete/<filename>', methods=['POST'])
def delete_file(filename):
    """
    删除一个已上传的文件。
    """
    try:
        filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
        os.remove(filepath)
        flash(f'文件 "{filename}" 已被成功删除。')
    except FileNotFoundError:
        flash(f'文件 "{filename}" 不存在，无法删除。')
    except Exception as e:
        flash(f'删除文件时发生错误: {e}')
    return redirect(url_for('files'))

@app.route('/edit/<filename>', methods=['GET', 'POST'])
def edit_file(filename):
    """
    编辑文件内容。
    """
    filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
    if not os.path.exists(filepath):
        flash(f'文件 "{filename}" 不存在。')
        return redirect(url_for('files'))

    if request.method == 'POST':
        content = request.form['content']
        try:
            with open(filepath, 'w', encoding='utf-8') as f:
                f.write(content)
            flash(f'文件 "{filename}" 已成功保存。')
            return redirect(url_for('view_file', filename=filename))
        except Exception as e:
            flash(f'保存文件时出错: {e}')
            return redirect(url_for('edit_file', filename=filename))

    try:
        with open(filepath, 'r', encoding='utf-8') as f:
            content = f.read()
    except Exception as e:
        content = f"无法读取文件内容: {e}"

    return render_template('edit.html', filename=filename, content=content)

# --- 命令管理路由 ---

@app.route('/commands')
def commands():
    """
    显示所有命令。
    """
    all_commands = _read_commands()
    return render_template('commands.html', commands=all_commands)

@app.route('/add_command', methods=['GET', 'POST'])
def add_command():
    """
    新增一个命令。
    """
    if request.method == 'POST':
        name = request.form.get('name')
        command = request.form.get('command')
        notes = request.form.get('notes')

        if not name or not command:
            flash('名称和命令是必填项。')
            return render_template('edit_command.html', command={'name': name, 'command': command, 'notes': notes})
        
        all_commands = _read_commands()
        new_command = {
            'id': str(uuid.uuid4()),
            'name': name,
            'command': command,
            'notes': notes
        }
        all_commands.append(new_command)
        _write_commands(all_commands)
        
        flash(f'命令 "{name}" 已成功添加。')
        return redirect(url_for('commands'))

    return render_template('edit_command.html')

@app.route('/edit_command/<command_id>', methods=['GET', 'POST'])
def edit_command(command_id):
    """
    编辑一个已存在的命令。
    """
    all_commands = _read_commands()
    command_to_edit = next((cmd for cmd in all_commands if cmd['id'] == command_id), None)

    if not command_to_edit:
        flash('找不到指定的命令。')
        return redirect(url_for('commands'))

    if request.method == 'POST':
        name = request.form.get('name')
        command = request.form.get('command')
        notes = request.form.get('notes')

        if not name or not command:
            flash('名称和命令是必填项。')
            # 回传用户已输入的数据
            return render_template('edit_command.html', command={'id': command_id, 'name': name, 'command': command, 'notes': notes})

        command_to_edit['name'] = name
        command_to_edit['command'] = command
        command_to_edit['notes'] = notes
        _write_commands(all_commands)
        
        flash(f'命令 "{name}" 已成功更新。')
        return redirect(url_for('commands'))

    return render_template('edit_command.html', command=command_to_edit)

@app.route('/delete_command/<command_id>', methods=['POST'])
def delete_command(command_id):
    """
    删除一个命令。
    """
    all_commands = _read_commands()
    command_to_delete = next((cmd for cmd in all_commands if cmd['id'] == command_id), None)

    if command_to_delete:
        all_commands.remove(command_to_delete)
        _write_commands(all_commands)
        flash(f'命令 "{command_to_delete["name"]}" 已被成功删除。')
    else:
        flash('找不到要删除的命令。')
        
    return redirect(url_for('commands'))

# --- API 路由 ---

@app.route('/api/links', methods=['GET'])
def get_links():
    # ... (保持不变)
    software_name = request.args.get('software')
    os_type = request.args.get('os')
    py_version_query = request.args.get('py_version')
    version_query = request.args.get('version')
    source_query = request.args.get('source')
    results = []
    softwares_to_iterate = software_data.items()
    if software_name and software_name in software_data:
        softwares_to_iterate = {software_name: software_data[software_name]}.items()
    elif software_name:
        return jsonify([])
    for software, details in softwares_to_iterate:
        if software == 'pip_mirrors': continue
        sources = details.get('source', {})
        for version_info in details.get('versions', []):
            current_py_version = version_info.get('py_version')
            current_version = version_info.get('version')
            if py_version_query and current_py_version != py_version_query: continue
            if version_query and current_version != version_query: continue
            for file_info in version_info.get('files', []):
                if os_type and file_info.get('os') != os_type: continue
                for source_name, base_url in sources.items():
                    if source_query and source_name != source_query: continue
                    url = base_url + file_info.get('file')
                    results.append({
                        'software': software,
                        'display_version': current_version or current_py_version,
                        'py_version': current_py_version,
                        'version': current_version,
                        'os': file_info.get('os'),
                        'arch': file_info.get('arch'),
                        'type': file_info.get('type'),
                        'source': source_name,
                        'url': url
                    })
    return jsonify(results)

@app.route('/api/pip-mirrors', methods=['GET'])
def get_pip_mirrors():
    """
    获取 PyPI 镜像配置。
    """
    return jsonify(software_data.get('pip_mirrors', {}))


@app.route('/api/options', methods=['GET'])
def get_options():
    """
    动态获取前端下拉菜单所需的所有可选值。
    """
    options = {'softwares': [], 'os_types': set(), 'sources': set(), 'software_specific': {}}
    software_specific_options = {}
    for software_name, details in software_data.items():
        if software_name == 'pip_mirrors': continue
        options['softwares'].append(software_name)
        current_software_oss = set()
        current_software_py_versions = set()
        current_software_versions = set()
        current_software_sources = set()
        for source_name in details.get('source', {}).keys():
            options['sources'].add(source_name)
            current_software_sources.add(source_name)
        for version_info in details.get('versions', []):
            if py_version := version_info.get('py_version'): current_software_py_versions.add(py_version)
            if version := version_info.get('version'): current_software_versions.add(version)
            for file_info in version_info.get('files', []):
                if os_type := file_info.get('os'):
                    options['os_types'].add(os_type)
                    current_software_oss.add(os_type)
        sorted_py_versions = sorted(list(current_software_py_versions), key=lambda x: [int(v) for v in x.split('.')])
        sorted_versions = sorted(list(current_software_versions))
        software_specific_options[software_name] = {
            'os_types': sorted(list(current_software_oss)),
            'py_versions': sorted_py_versions,
            'versions': sorted_versions,
            'sources': sorted(list(current_software_sources))
        }
    options['softwares'] = sorted(options['softwares'])
    options['os_types'] = sorted(list(options['os_types']))
    options['sources'] = sorted(list(options['sources']))
    options['software_specific'] = software_specific_options
    return jsonify(options)

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=5080)